package top.cluski.feieprint.api.maiyatian;

import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.annotation.JSONField;
import com.dtflys.forest.Forest;
import jakarta.annotation.Resource;
import lombok.Data;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import top.cluski.feieprint.api.maiyatian.entity.*;
import top.cluski.feieprint.config.MaiyatianConfig;
import top.cluski.feieprint.entity.MaiyationTokenEntity;
import top.cluski.feieprint.mapper.MaiyatianTokenMapper;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.*;

@Slf4j
@Component
public class MaiyatianApi {
    @Resource
    private MaiyatianConfig maiyatianConfig;

    @Resource
    MaiyatianTokenMapper maiyatianTokenMapper;

    private static final String ACCESS_TOKEN_COMMAND = "access_token";
    private static final String REFRESH_TOKEN_COMMAND = "refresh_token";
    private static final String ORDER_LIST_COMMAND = "order_list";


    public AccessTokenResponse getAccessToken(String code) {
        HashMap<String, Object> body = new HashMap<>();
        body.put("app_key", maiyatianConfig.getAppKey());
        body.put("request_id", UUID.fastUUID().toString());
        body.put("data", JSONObject.toJSONString(createAccessTokenRequest(code)));
        body.put("timestamp", System.currentTimeMillis());
        body.put("token", "");
        body.put("signature", SignEntity.fromMap(ACCESS_TOKEN_COMMAND, body).generateSign(maiyatianConfig.getAppSecret()));

        GenericResponse resp = Forest.post(maiyatianConfig.getBaseUrl() + ACCESS_TOKEN_COMMAND)
                .contentTypeJson()
                .addBody(body)
                .execute(GenericResponse.class);

        if (resp.getCode() != 200) {
            log.error("调用麦芽田{}失败 resp={}", ACCESS_TOKEN_COMMAND, resp);
            throw new RuntimeException(resp.getMsg());
        }

        return JSONObject.parseObject(resp.getData(), AccessTokenResponse.class);
    }

    public AccessTokenResponse refreshToken(String token, String refreshToken) {
        HashMap<String, Object> body = new HashMap<>();
        body.put("app_key", maiyatianConfig.getAppKey());
        body.put("request_id", UUID.fastUUID().toString());
        body.put("data", JSONObject.toJSONString(createRefreshTokenRequest(token, refreshToken)));
        body.put("timestamp", System.currentTimeMillis());
        body.put("token", token);
        body.put("signature", SignEntity.fromMap(REFRESH_TOKEN_COMMAND, body).generateSign(maiyatianConfig.getAppSecret()));

        GenericResponse resp = Forest.post(maiyatianConfig.getBaseUrl() + REFRESH_TOKEN_COMMAND)
                .contentTypeJson()
                .addBody(body)
                .execute(GenericResponse.class);

        if (resp.getCode() != 200) {
            log.error("调用麦芽田{}失败 resp={}", REFRESH_TOKEN_COMMAND, resp);
            throw new RuntimeException(resp.getMsg());
        }

        return JSONObject.parseObject(resp.getData(), AccessTokenResponse.class);
    }

    public JSONObject getOrderList() {
        HashMap<String, Object> body = new HashMap<>();
        body.put("app_key", maiyatianConfig.getAppKey());
        body.put("request_id", UUID.fastUUID().toString());
        body.put("data", JSONObject.toJSONString(createOrderListRequest()));
        body.put("timestamp", System.currentTimeMillis());
        body.put("token", getToken());
        body.put("signature", SignEntity.fromMap(ORDER_LIST_COMMAND, body).generateSign(maiyatianConfig.getAppSecret()));

        GenericResponse resp = Forest.post(maiyatianConfig.getBaseUrl() + ORDER_LIST_COMMAND)
                .contentTypeJson()
                .addBody(body)
                .execute(GenericResponse.class);

        if (resp.getCode() != 200) {
            log.error("调用麦芽田{}失败 resp={}", ORDER_LIST_COMMAND, resp);
            throw new RuntimeException(resp.getMsg());
        }

        return JSONObject.parseObject(resp.getData());
    }

    private AccessTokenRequest createAccessTokenRequest(String code) {
        AccessTokenRequest accessTokenRequest = new AccessTokenRequest();
        accessTokenRequest.setCode(code);
        accessTokenRequest.setShopId("10001");
        accessTokenRequest.setCategory("shaokao");
        accessTokenRequest.setName("测试店铺");
        accessTokenRequest.setType("waimai");
        accessTokenRequest.setLongitude("103.123123123");
        accessTokenRequest.setLatitude("90.123123123");
        return accessTokenRequest;
    }

    private RefreshTokenRequest createRefreshTokenRequest(String token, String refreshToken) {
        RefreshTokenRequest refreshTokenRequest = new RefreshTokenRequest();
        refreshTokenRequest.setToken(token);
        refreshTokenRequest.setRefreshToken(refreshToken);
        return refreshTokenRequest;
    }

    private OrderListRequest createOrderListRequest() {
        OrderListRequest orderListRequest = new OrderListRequest();
        orderListRequest.setShopId("10001");
        orderListRequest.setStartTime(System.currentTimeMillis() - 60 * 60 * 24 * 1000L);
        orderListRequest.setEndTime(System.currentTimeMillis());
        return orderListRequest;
    }

    private String getToken() {
        MaiyationTokenEntity maiyationTokenEntity = maiyatianTokenMapper.selectOneById(1);
        if (maiyationTokenEntity == null) {
            throw new RuntimeException("麦芽田token不存在");
        }
        return maiyationTokenEntity.getToken();
    }

    @Data
    private static class SignEntity {
        String command;
        String data;
        @JSONField(name = "app_key")
        String appKey;
        String token;
        Long timestamp;

        public static SignEntity fromMap(String command, Map<String, Object> map) {
            SignEntity signEntity = new SignEntity();
            signEntity.setCommand(command);
            signEntity.setData((String) map.get("data"));
            signEntity.setAppKey((String) map.get("app_key"));
            signEntity.setToken(StrUtil.isNotBlank((String) map.get("token")) ? (String) map.get("token") : "");
            signEntity.setTimestamp((long) map.get("timestamp"));
            return signEntity;
        }

        @SneakyThrows
        public String generateSign(String key) {
            TreeMap<String, Object> map = new TreeMap<>(String::compareTo);
            map.put("command", this.command);
            map.put("data", this.data);
            map.put("app_key", this.appKey);
            map.put("token", this.token);
            map.put("timestamp", this.timestamp);

            StringJoiner sb = new StringJoiner(",");
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                if (entry.getValue() != null) {
                    sb.add(entry.getKey() + "=" + entry.getValue());
                }
            }

            String sign = calSignature(key, sb.toString());
            System.out.println("stringToSign" + sb.toString());
            System.out.println("sign=" + sign);
            return sign;
        }

        private String calSignature(String appSecret, String dataToSign) throws Exception {
            SecretKeySpec secretKeySpec = new SecretKeySpec(appSecret.getBytes(), "HmacSHA256");
            Mac sha256HMAC = Mac.getInstance("HmacSHA256");
            sha256HMAC.init(secretKeySpec);
            byte[] hmacResult = sha256HMAC.doFinal(dataToSign.getBytes());
            Base64.Encoder encoder = Base64.getUrlEncoder();
            byte[] base64Result = encoder.encode(hmacResult);
            return new String(base64Result);
        }
    }


}
